package com.mzz.esdemo.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mzz.esdemo.common.constant.EsConstant;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.*;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.index.reindex.ReindexRequest;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Why
 * 2022-01-03
 */
@Service
@RequiredArgsConstructor
public class DocumentService {

    private static final String ID = "id";
    private final RestHighLevelClient restHighLevelClient;

    /**
     * Create doc.
     *
     * @param index  the index
     * @param id     the id
     * @param source the source
     * @return the doc write response
     */
    @SneakyThrows
    public DocWriteResponse createDoc(String index, String id, Object source) {
        IndexRequest indexRequest = new IndexRequest(index)
                .id(id)
                .source(toJsonString(source), XContentType.JSON);
        return restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
    }

    /**
     * Upsert doc.
     *
     * @param index  the index
     * @param id     the id
     * @param source the source
     * @return the doc write response
     */
    @SneakyThrows
    public DocWriteResponse upsertDoc(String index, String id, Object source) {
        String jsonString = toJsonString(source);
        UpdateRequest updateRequest = new UpdateRequest(index, id)
                .doc(jsonString, XContentType.JSON)
                .upsert(jsonString, XContentType.JSON);
        return restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
    }

    /**
     * Upsert doc by bulk.
     *
     * @param index   the index
     * @param sources the sources
     * @return the bulk response
     */
    @SneakyThrows
    public BulkResponse upsertDocByBulk(String index, List<JSONObject> sources) {
        BulkRequest bulkRequest = new BulkRequest(index);
        for (JSONObject source : sources) {
            String jsonString = toJsonString(source);
            UpdateRequest updateRequest = new UpdateRequest(index, source.getString(ID))
                    .doc(jsonString, XContentType.JSON)
                    .upsert(jsonString, XContentType.JSON);
            bulkRequest.add(updateRequest);
        }
        return restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
    }

    /**
     * Gets doc.
     *
     * @param index the index
     * @param id    the id
     * @return the doc
     */
    @SneakyThrows
    public GetResponse getDoc(String index, String id) {
        GetRequest deleteRequest = new GetRequest(index, id);
        return restHighLevelClient.get(deleteRequest, RequestOptions.DEFAULT);
    }

    /**
     * Delete doc.
     *
     * @param index the index
     * @param id    the id
     * @return the doc write response
     */
    @SneakyThrows
    public DocWriteResponse deleteDoc(String index, String id) {
        DeleteRequest deleteRequest = new DeleteRequest(index, id);
        return restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
    }

    /**
     * Delete by query.
     *
     * @param index     the index
     * @param queryJson the query json
     * @return the bulk by scroll response
     */
    @SneakyThrows
    public BulkByScrollResponse deleteByQuery(String index, String queryJson) {
        DeleteByQueryRequest deleteRequest = new DeleteByQueryRequest(index)
                .setQuery(QueryBuilders.wrapperQuery(queryJson));
        return restHighLevelClient.deleteByQuery(deleteRequest, RequestOptions.DEFAULT);
    }

    /**
     * Clear index.
     *
     * @param index the index
     * @return the bulk by scroll response
     */
    @SneakyThrows
    public BulkByScrollResponse clearIndex(String index) {
        return deleteByQuery(index, QueryBuilders.matchAllQuery().toString());
    }

    /**
     * Reindex.
     *
     * @param sourceIndex the source index
     * @param destIndex   the dest index
     * @return the bulk by scroll response
     */
    @SneakyThrows
    public BulkByScrollResponse reindex(String sourceIndex, String destIndex) {
        return restHighLevelClient.reindex(new ReindexRequest()
                .setSourceIndices(sourceIndex)
                .setDestIndex(destIndex), RequestOptions.DEFAULT);
    }

    /**
     * To json string.
     *
     * @param obj the obj
     * @return the string
     */
    private static String toJsonString(Object obj) {
        return JSON.toJSONString(obj);
    }

    /**
     * Reindex.
     *
     * @param sourceIndex the source index
     * @param destIndex   the dest index
     * @return the bulk by scroll response
     */
    @SneakyThrows
    public List<Map<String, Object>> searchAfter(Integer pageNun,String type){
        System.out.println("--------------------使用search_after搜索文档---------------------");
        SearchRequest searchRequest = new SearchRequest(EsConstant.INDEX_NAME);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //设置查询大小
        // 第几页
        searchSourceBuilder.from(0);
        searchSourceBuilder.size(100);
        //设置唯一排序值定位

        //termQuery精确查询
        //
 /*       MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders
                .matchPhraseQuery("name", "张三10");
        //时间范围的设定
        RangeQueryBuilder rangequerybuilder = QueryBuilders
                //传入时间，目标格式2020-01-02T03:17:37.638Z
                .rangeQuery("updateDate")
                .from("2021-090-16").to("2021-09-16");
        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(matchPhraseQueryBuilder).must(rangequerybuilder);*/
        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.boolQuery()
                        .filter(QueryBuilders.matchPhraseQuery("name","*张三*"))).must(QueryBuilders.boolQuery()).
                        filter(QueryBuilders.rangeQuery("updateDate").from("2021-09-16").to("2021-09-16"));
/*        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.matchQuery("disabled", 0))
                .must(QueryBuilders.matchQuery("tablestatus", 0))
                .must(QueryBuilders.matchQuery("type", TABLE_TYPE))
                .must(query);*/
        //生成DSL查询语句
        searchSourceBuilder.query(queryBuilder);
        searchSourceBuilder.sort("_id", SortOrder.ASC);
        searchRequest.source(searchSourceBuilder);

        List<Map<String, Object>> result = new ArrayList<>();
        Boolean flag = false;
        if (type.equals("no")){
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                //获取需要数据
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                result.add(sourceAsMap);
                System.out.println("返回的数据"+sourceAsMap.get("id"));
            }
            System.out.println("返回的数据量"+result.size());
            flag = true;
            return result;
        }else {
            //设置searchAfter的最后一个排序值
            Object[] lastNum = new Object[]{pageNun};
            searchSourceBuilder.searchAfter(lastNum);
            searchRequest.source(searchSourceBuilder);

            //进行下次查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                //获取需要数据
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                result.add(sourceAsMap);
                System.out.println("返回的数据"+sourceAsMap.get("id"));
            }
            System.out.println("返回的数据量"+result.size());
            flag = true;
            return result;
        }
    }


    /**
     * Reindex.
     *
     * @param sourceIndex the source index
     * @param destIndex   the dest index
     * @return the bulk by scroll response
     */
    @SneakyThrows
    public void searchScroll(){
/*        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        boolQuery.should(QueryBuilders.termQuery("name","*张三*"));*/
        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.boolQuery()
                        .filter(QueryBuilders.matchPhraseQuery("name","*张三*"))).must(QueryBuilders.boolQuery());
        // 存活时间，当索引数据量特别大时，出现超时可能性大，此值适当调大
        String index = EsConstant.INDEX_NAME;
        Scroll scroll = new Scroll(TimeValue.timeValueMinutes(10L));
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(queryBuilder);
        searchSourceBuilder.size(100);
        SearchRequest searchRequest = new SearchRequest()
                // ES7已经去掉type，查询时加type
                .indices(index)
                .scroll(scroll)
                .source(searchSourceBuilder);
        SearchResponse searchResponse = null;
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String scrollId = searchResponse.getScrollId();
        SearchHit[] searchHits = searchResponse.getHits().getHits();
        for (SearchHit searchHit : searchHits) {
            System.out.println("第一次："+searchHit.getSourceAsString());
        }
        //遍历搜索命中的数据，直到没有数据
        while (searchHits != null && searchHits.length > 0) {
            System.out.println("scrollId"+scrollId);
            SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
            scrollRequest.scroll(scroll);
            try {
                searchResponse = restHighLevelClient.scroll(scrollRequest, RequestOptions.DEFAULT);
            } catch (IOException e) {
                e.printStackTrace();
            }
            scrollId = searchResponse.getScrollId();
            System.out.println("scrollId2"+scrollId);
            searchHits = searchResponse.getHits().getHits();
            if (searchHits != null && searchHits.length > 0) {
                for (SearchHit searchHit : searchHits) {
                    System.out.println("第二次："+searchHit.getSourceAsString());
                }
            }
        }
        //clean scroll
        ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
        clearScrollRequest.addScrollId(scrollId);
        ClearScrollResponse clearScrollResponse = null;
        try {
            clearScrollResponse = restHighLevelClient.clearScroll(clearScrollRequest,RequestOptions.DEFAULT);
        } catch (IOException e) {
            System.out.println("clear-scroll-error:{}"+e);
        }
        boolean succeeded = clearScrollResponse.isSucceeded();
        System.out.println(succeeded);
    }

    /**
     * Reindex.
     *
     * @param sourceIndex the source index
     * @param destIndex   the dest index
     * @return the bulk by scroll response
     */
    @SneakyThrows
    public void searchFromSize(){
        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.boolQuery()
                        .filter(QueryBuilders.matchPhraseQuery("name","*张三*"))).must(QueryBuilders.boolQuery());
        // 存活时间，当索引数据量特别大时，出现超时可能性大，此值适当调大
        String index = EsConstant.INDEX_NAME;
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(queryBuilder);
        searchSourceBuilder.from(2);
        searchSourceBuilder.size(100);
        SearchRequest searchRequest = new SearchRequest()
                // ES7已经去掉type，查询时加type
                .indices(index)
                .source(searchSourceBuilder);
        SearchResponse searchResponse = null;
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        SearchHit[] searchHits = searchResponse.getHits().getHits();
        for (SearchHit searchHit : searchHits) {
            System.out.println("第一次："+searchHit.getSourceAsString());
        }
    }
}
